package com.gitee.cui.client.client;

import com.gitee.cui.client.command.*;
import com.gitee.cui.client.sender.ChatSender;
import com.gitee.cui.client.sender.LoginSender;
import com.gitee.cui.common.cocurrent.FutureTaskScheduler;
import com.gitee.cui.common.common.bean.User;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.EventLoop;
import io.netty.util.concurrent.GenericFutureListener;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
import java.util.concurrent.TimeUnit;

/**
 * 命令处理器
 * @author cuiqingqiang
 * @version 1.0
 * @date 2021/10/27 14:04
 **/
@Slf4j
@Data
@Service("commandController")
public class CommandController {

    /**
     * 登录命令收集器
     */
    @Autowired
    private LoginConsoleCommand loginConsoleCommand;

    /**
     * 命令菜单收集器
     */
    @Autowired
    private ClientCommandMenu clientCommandMenu;

    /**
     * 聊天内容收集器
     */
    @Autowired
    private ChatConsoleCommand chatConsoleCommand;

    /**
     * 退出命令收集器
     */
    @Autowired
    private LogoutConsoleCommand logoutConsoleCommand;

    @Autowired
    private LoginSender loginSender;

    @Autowired
    private ChatSender chatSender;

    @Autowired
    private ChatNettyClient chatNettyClient;

    /**
     * 启动命令收集线程
     */
    public void startCommandThread() {
        Thread.currentThread().setName("命令线程");
        while (true) {
            while (!connectFlag) {
                // 连接聊天服务端
                startConnectServer();
                // 等待命令线程
                waitCommandThread();
            }
            // 处理命令
            while (session != null) {
                Scanner scanner = new Scanner(System.in);
                clientCommandMenu.exec(scanner);
                String input = clientCommandMenu.getCommandInput();

                // 校验命令集合中是否存在用户输入的命令
                BaseCommand command = commandMap.get(input);
                if (command == null) {
                    System.err.println(String.format("无法识别[%s]指令，请重新输入！", input));
                    break;
                }

                // 根据输入的指令，执行相应的逻辑
                switch (input) {
                    case LoginConsoleCommand.KEY:
                        // 用户登录指令
                        command.exec(scanner);
                        // 开始登录逻辑
                        startLogin((LoginConsoleCommand) command);
                        break;
                    case ChatConsoleCommand.KEY:
                        // 聊天指令
                        command.exec(scanner);
                        startChat((ChatConsoleCommand) command);
                        break;
                    case LogoutConsoleCommand.KEY:
                        // 退出指令
                        command.exec(scanner);
                        startLogout((LogoutConsoleCommand) command);
                        break;
                    default:
                        break;
                }
            }
        }
    }

    /**
     * 开始连接聊天服务端
     */
    public void startConnectServer() {
        FutureTaskScheduler.add(()-> {
            chatNettyClient.setListener(connectedListener);
            chatNettyClient.doConnect();
        });
    }

    /**
     * 唤醒命令收集线程
     */
    public synchronized void notifyCommandThread() {
        // 唤醒，命令收集程
        this.notify();
    }

    /**
     * 使命令收集线程等待，等待被唤醒
     */
    public synchronized void waitCommandThread() {
        //休眠，命令收集线程
        try {
            this.wait();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    /**
     * 用户退出逻辑
     * @param command 退出收集器
     */
    private void startLogout(LogoutConsoleCommand command) {
        if (!isLogin()) {
            log.info("用户还没有登录，请先登录");
            return;
        }

        // 用户登出，与聊天服务端断开连接并清理session信息
    }

    /**
     * 获取到聊天信息后，开始聊天
     * @param command 聊天指令收集器，包含聊天对方的id和聊天信息
     */
    private void startChat(ChatConsoleCommand command) {
        if (!isLogin()) {
            // 用户还未登录
            log.info("用户还没有登录，请先登录");
            return;
        }

        // 聊天发送器，开始向用户发送聊天消息
        chatSender.setSession(session);
        chatSender.setUser(user);
        chatSender.sendChatMsg(command.getToUserId(), command.getMessage());
    }

    /**
     * 获取到用户信息后，开始进行登录操作
     * @param command 登录指令，包含用户名密码数据
     */
    private void startLogin(LoginConsoleCommand command) {
        // 校验是否连接通讯服务端
        if (!isConnectFlag()) {
            log.info("连接异常，请重新建立连接");
            return;
        }

        User user = new User();
        user.setUid(command.getUserName());
        user.setToken(command.getPassword());
        user.setDevId("1111");
        this.user = user;
        // 向客户端session中写入用户信息
        session.setUser(user);
        // 发送用户登录请求，LoginSender的业务逻辑
        loginSender.setSession(session);
        loginSender.setUser(user);
        loginSender.sendLoginMsg();
    }

    /**
     * 初始化命令映射集合
     */
    public void initCommandMap() {
        commandMap = new HashMap<>();
        commandMap.put(loginConsoleCommand.getKey(), loginConsoleCommand);
        commandMap.put(clientCommandMenu.getKey(), clientCommandMenu);
        commandMap.put(chatConsoleCommand.getKey(), chatConsoleCommand);
        commandMap.put(logoutConsoleCommand.getKey(), logoutConsoleCommand);

        clientCommandMenu.setAllCommand(commandMap);
    }

    /**
     * 判断用户是否登录
     * @return 用户是否登录
     */
    private boolean isLogin() {
        if (session == null) {
            log.info("session is null");
            return false;
        }
        return session.isLogin();
    }

    /**
     * 是否连接聊天服务端标识，false：未连接服务端，true：已连接服务端
     */
    private boolean connectFlag = false;

    /**
     * 用户信息
     */
    private User user;

    /**
     * 命令映射集合
     */
    private Map<String, BaseCommand> commandMap;

    /**
     * 客户端session信息
     */
    private ClientSession session;

    private Channel channel;

    GenericFutureListener<ChannelFuture> closeListener = (ChannelFuture f) -> {
        log.info(new Date() + ": 连接已经断开……");
        channel = f.channel();

        // 创建会话
        ClientSession session =
                channel.attr(ClientSession.SESSION_KEY).get();
        session.close();

        //唤醒用户线程
        notifyCommandThread();
    };

    GenericFutureListener<ChannelFuture> connectedListener = (ChannelFuture f) -> {
        final EventLoop eventLoop = f.channel().eventLoop();
        if (!f.isSuccess()) {
            log.info("连接失败！在10s之后尝试重连！");
            eventLoop.schedule(() -> chatNettyClient.doConnect(), 10, TimeUnit.SECONDS);
            connectFlag = false;
        } else {
            connectFlag = true;
            log.info("聊天服务器 连接成功!");
            channel = f.channel();

            // 创建会话
            session = new ClientSession(channel);
            session.setConnected(true);
            channel.closeFuture().addListener(closeListener);

            //唤醒用户线程
            notifyCommandThread();
        }
    };
}
